#ifndef __BLOCK_QUEUE_HPP__
#define __BLOCK_QUEUE_HPP__
#include <iostream>
#include <string>
#include <pthread.h>
#include <queue>
#include <unistd.h>

using namespace std;

template <class T>
class BlockQueue
{
private:
    bool IsFull()
    {
        return _block_queue.size() == _cap;
    }
    bool IsEmpty()
    {
        return _block_queue.empty();
    }
public:
    BlockQueue(int cap):_cap(cap)
    {
        _productor_wait_num = 0;
        _conmuser_wait_num = 0;
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_productor_cond, nullptr);
        pthread_cond_init(&_conmuser_cond, nullptr);
    }

    void Enqueue(T &in)
    {
        pthread_mutex_lock(&_mutex);
        while(IsFull())
        {
            _productor_wait_num++;
            pthread_cond_wait(&_productor_cond, &_mutex);
            _productor_wait_num--;
        }
        //生产
        _block_queue.push(in);
        if(_conmuser_wait_num > 0)
        {
            pthread_cond_signal(&_conmuser_cond);
        }

        pthread_mutex_unlock(&_mutex);
    }

    void Pop(T &out)
    {
        pthread_mutex_lock(&_mutex);
        while(IsEmpty())
        {
            _conmuser_wait_num++;
            pthread_cond_wait(&_conmuser_cond, &_mutex);
            _conmuser_wait_num--;
        }
        //消费
        out = _block_queue.front();
        _block_queue.pop();
        if(_productor_wait_num > 0)
        {
            pthread_cond_signal(&_productor_cond);
        }

        pthread_mutex_unlock(&_mutex);
    }


    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_productor_cond);
        pthread_cond_destroy(&_conmuser_cond);
    }
private:
    queue<T> _block_queue;
    int _cap;
    pthread_mutex_t _mutex;
    pthread_cond_t _productor_cond;
    pthread_cond_t _conmuser_cond;
    int _productor_wait_num;
    int _conmuser_wait_num;
};


#endif